/**
 * @author nttdocomo
 */
define(function(require) {
	require("../../aries");
	require("../../component");
	require("../labelable");
	require("./field");
	/**
	 Creates a new form field base.
	 @class Represents a field of basic.
	 @param {Object} options
	 */
	aries.klass("aries.form.field.Base", aries.Component.extend({
		tagName : 'div',
		inputType : 'text',
		fieldCls : aries.baseCSSPrefix + 'form-field',
		focusCls : 'form-focus',
		validateOnBlur : true,
		inputType : 'text',
		width : '100%',
		readOnly : false,
		events : {},
		classMap : {},
		classList : [],
		baseCls : aries.baseCSSPrefix + "field",
		fieldSubTpl : '<input id="<%=id%>" type="<%=type%>"<% if(typeof(name)!=="undefined"){ %> name="<%=name%>"<%}%><% if(typeof(value)!=="undefined"){ %> value="<%=value%>"<%}%><% if(typeof(placeholder)!=="undefined"){ %> placeholder="{placeholder}"<%}%><% if(typeof(maxlength)!=="undefined"){ %> maxlength="{maxLength}"<%}%><% if(typeof(readonly)!=="undefined"){ %> readonly="readonly"<%}%><% if(typeof(disabled)!=="undefined"){ %> disabled="disabled"<%}%><% if(typeof(tabIndex)!=="undefined"){ %> tabIndex="{tabIdx}"<%}%><% if(typeof(fieldStyle)!=="undefined"){ %> style="<%=fieldStyle%>"<%}%> class="<% if(typeof(fieldCls)!=="undefined"){ %><%=fieldCls%> <% } %><% if(typeof(typeCls)!=="undefined"){ %><%=typeCls%> <% } %><% if(typeof(editableCls)!=="undefined"){ %><%=editableCls%><% } %>" autocomplete="off"/>',
		checkChangeEvents : $.browser.msie && (!document.documentMode || document.documentMode < 9) ? ['change', 'propertychange'] : ['change', 'input', 'textInput', 'keyup', 'dragdrop'],
		/*addCls : function(cls) {
		var map = this.classMap;
		if(!map[cls]) {
		map[cls] = true;
		this.classList.push(cls)
		}
		},*/

		/**
		 * Clear any invalid styles/messages for this field.
		 *
		 * **Note**: this method does not cause the Field's {@link #validate} or {@link #isValid} methods to return `true`
		 * if the value does not _pass_ validation. So simply clearing a field's errors will not necessarily allow
		 * submission of forms submitted with the {@link Ext.form.action.Submit#clientValidation} option set.
		 */
		clearInvalid : function() {
			// Clear the message and fire the 'valid' event
			var hadError = this.hasActiveError();
			this.unsetActiveError();
			if (hadError) {
				this.updateLayout();
			}
		},
		applyRenderSelectors : function() {
			aries.Component.prototype.applyRenderSelectors.apply(this, arguments)
			/**
			 * @property {Ext.Element} inputEl
			 * The input Element for this Field. Only available after the field has been rendered.
			 */
			this.inputEl = this.$el.find('#' + this.getInputId());
		},

		beforeRender : function() {
			aries.Component.prototype.beforeRender.call(this, arguments);
			this.beforeLabelableRender(arguments);
			if (this.readOnly) {
				this.addCls(this.readOnlyCls);
			}
		},

		/**
		 * Returns the value that would be included in a standard form submit for this field. This will be combined with the
		 * field's name to form a name=value pair in the {@link #getSubmitData submitted parameters}. If an empty string is
		 * returned then just the name= will be submitted; if null is returned then nothing will be submitted.
		 *
		 * Note that the value returned will have been {@link #processRawValue processed} but may or may not have been
		 * successfully {@link #validate validated}.
		 *
		 * @return {String} The value to be submitted, or null.
		 */
		getSubmitValue : function() {
			return this.processRawValue(this.getRawValue());
		},
		initEvents : function() {
			var events = {};
			this.onChangeEvent = this.checkChange;
			for (var i = 0; i < this.checkChangeEvents.length; i++) {
				events[this.checkChangeEvents[i] + ' input'] = this.onChangeEvent;
			}
			$.extend(this.events, events)
		},
		/**
		 * @private
		 */
		initialize : function(options) {
			//options = $.extend({}, this.initRenderData(), this.getSubTplData(), options);
			aries.Component.prototype.initialize.apply(this, arguments);
		},
		initComponent : function() {
			aries.Component.prototype.initComponent.apply(this, arguments);
			this.subTplData = this.subTplData || {};
			this.initEvents();
			this.initLabelable();
			this.initField();
		},
		initRenderData : function() {
			var config = aries.Component.prototype.initRenderData.apply(this, arguments);
			return $.extend(config, this.getLabelableRenderData());
		},

		initRenderTpl : function() {
			if (!this.hasOwnProperty('renderTpl')) {
				this.renderTpl = this.labelableRenderTpl;
			}
			return aries.Component.prototype.initRenderTpl.apply(this, arguments);
		},

		/**
		 * Returns whether or not the field value is currently valid by {@link #getErrors validating} the
		 * {@link #processRawValue processed raw value} of the field. **Note**: {@link #disabled} fields are
		 * always treated as valid.
		 *
		 * @return {Boolean} True if the value is valid, else false
		 */
		isValid : function() {
			var disabled = this.disabled, validate = this.forceValidation || !disabled;

			return validate ? this.validateValue(this.processRawValue(this.getRawValue())) : disabled;
		},

		getFocusEl : function() {
			return this.inputEl;
		},
		/**
		 * Returns the input id for this field. If none was specified via the {@link #inputId} config, then an id will be
		 * automatically generated.
		 */
		getInputId : function() {
			return this.inputId || (this.inputId = this.id + '-inputEl');
		},

		/**
		 * Returns the raw value of the field, without performing any normalization, conversion, or validation. To get a
		 * normalized and converted value see {@link #getValue}.
		 * @return {String} value The raw String value of the field
		 */
		getRawValue : function() {
			var v = (this.inputEl ? this.inputEl.val() : aries.value(this.rawValue, ''));
			this.rawValue = v;
			return v;
		},
		getSubTplData : function() {
			var type = this.inputType, inputId = this.getInputId(), data = $.extend({
				id : inputId,
				cmpId : this.id,
				name : this.name || inputId,
				//disabled : me.disabled,
				//readOnly : me.readOnly,
				value : this.getRawValue(),
				type : type,
				fieldCls : this.fieldCls,
				//fieldStyle : me.getFieldStyle(),
				//tabIdx : me.tabIndex,
				typeCls : aries.baseCSSPrefix + 'form-' + (type === 'password' ? 'text' : type)
			}, this.subTplData);
			return data;
		},
		getSubTplMarkup : function() {
			return this.getTpl('fieldSubTpl')(this.getSubTplData());
			//(new this.fieldSubTpl(this.getSubTplData())).html();
		},

		/**
		 * Returns the current data value of the field. The type of value returned is particular to the type of the
		 * particular field (e.g. a Date object for {@link Ext.form.field.Date}), as the result of calling {@link #rawToValue} on
		 * the field's {@link #processRawValue processed} String value. To return the raw String value, see {@link #getRawValue}.
		 * @return {Object} value The field value
		 */
		getValue : function() {
			var val = this.rawToValue(this.processRawValue(this.getRawValue()));
			this.value = val;
			return val;
		},

		/**
		 * Display one or more error messages associated with this field, using {@link #msgTarget} to determine how to
		 * display the messages and applying {@link #invalidCls} to the field's UI element.
		 *
		 * **Note**: this method does not cause the Field's {@link #validate} or {@link #isValid} methods to return `false`
		 * if the value does _pass_ validation. So simply marking a Field as invalid will not prevent submission of forms
		 * submitted with the {@link Ext.form.action.Submit#clientValidation} option set.
		 *
		 * @param {String/String[]} errors The validation message(s) to display.
		 */
		markInvalid : function(errors) {
			// Save the message and fire the 'invalid' event
			var me = this, oldMsg = me.getActiveError();
			me.setActiveErrors($.makeArray(errors));
			if (oldMsg !== me.getActiveError()) {
				me.updateLayout();
			}
		},

		/**
		 * Performs any necessary manipulation of a raw field value to prepare it for {@link #rawToValue conversion} and/or
		 * {@link #validate validation}, for instance stripping out ignored characters. In the base implementation it does
		 * nothing; individual subclasses may override this as needed.
		 *
		 * @param {Object} value The unprocessed string value
		 * @return {Object} The processed string value
		 */
		processRawValue : function(value) {
			return value;
		},

		/**
		 * Converts a raw input field value into a mixed-type value that is suitable for this particular field type. This
		 * allows controlling the normalization and conversion of user-entered values into field-type-appropriate values,
		 * e.g. a Date object for {@link Ext.form.field.Date}, and is invoked by {@link #getValue}.
		 *
		 * It is up to individual implementations to decide how to handle raw values that cannot be successfully converted
		 * to the desired object type.
		 *
		 * See {@link #valueToRaw} for the opposite conversion.
		 *
		 * The base implementation does no conversion, returning the raw value untouched.
		 *
		 * @param {Object} rawValue
		 * @return {Object} The converted value.
		 */
		rawToValue : function(rawValue) {
			return rawValue;
		},

		/**
		 * @private Overrides the method from the aries.form.Labelable mixin to also add the invalidCls to the inputEl,
		 * as that is required for proper styling in IE with nested fields (due to lack of child selector)
		 */
		renderActiveError : function() {
			var hasError = this.hasActiveError();
			if (this.inputEl) {
				// Add/remove invalid class
				this.inputEl[hasError ? 'addClass' : 'removeClass'](this.invalidCls + '-field');
			}
			this.mixins.labelable.prototype.renderActiveError.call(this);
		},

		/**
		 * Set the active error message to an Array of error messages. The messages are formatted into a single message
		 * string using the {@link #activeErrorsTpl}. Also see {@link #setActiveError} which allows setting the entire error
		 * contents with a single string. Note that this only updates the error message element's text and attributes,
		 * you'll have to call doComponentLayout to actually update the field's layout to match. If the field extends
		 * {@link Ext.form.field.Base} you should call {@link Ext.form.field.Base#markInvalid markInvalid} instead.
		 * @param {String[]} errors The error messages
		 */
		setActiveErrors : function(errors) {
			errors = _.clone(errors);
			this.activeError = errors[0];
			this.activeErrors = errors;
			this.activeError = this.getTpl('activeErrorsTpl')({
				errors : errors
			});
			this.renderActiveError();
		},

		/**
		 * Sets the field's raw value directly, bypassing {@link #valueToRaw value conversion}, change detection, and
		 * validation. To set the value with these additional inspections see {@link #setValue}.
		 * @param {Object} value The value to set
		 * @return {Object} value The field value that is set
		 */
		setRawValue : function(value) {
			var me = this;
			value = aries.value(value, '');
			this.rawValue = value;

			// Some Field subclasses may not render an inputEl
			if (this.inputEl) {
				this.inputEl.val(value);
			}
			return value;
		},
		/**
		 * @public
		 * @param {String|Number} value
		 */
		setValue : function(value) {
			this.setRawValue(this.valueToRaw(value));
			return this.mixins.field.prototype.setValue.call(this, value);
			//return aries.form.field.Field.setValue.call(this, value);
		},
		/**
		 * @private
		 */
		validateValue : function(value) {
			var errors = this.getErrors(value), isValid = aries.isEmpty(errors);
			if (!this.preventMark) {
				if (isValid) {
					this.clearInvalid();
				} else {
					this.markInvalid(errors);
				}
			}

			return isValid;
		},

		/**
		 * Converts a mixed-type value to a raw representation suitable for displaying in the field. This allows controlling
		 * how value objects passed to {@link #setValue} are shown to the user, including localization. For instance, for a
		 * {@link Ext.form.field.Date}, this would control how a Date object passed to {@link #setValue} would be converted
		 * to a String for display in the field.
		 *
		 * See {@link #rawToValue} for the opposite conversion.
		 *
		 * The base implementation simply does a standard toString conversion, and converts {@link Ext#isEmpty empty values}
		 * to an empty string.
		 *
		 * @param {Object} value The mixed-type value to convert to the raw representation.
		 * @return {Object} The converted raw value.
		 */
		valueToRaw : function(value) {
			return '' + aries.value(value, '');
		}
	})).mixin({
		labelable : aries.form.Labelable,
		field : aries.form.field.Field
	})
});
